package com.google.code.sfa.parse;


import com.google.code.sfa.util.*;
import org.biojava.bio.symbol.SymbolList;
import org.biojava.bio.symbol.AbstractSymbolList;
import org.biojava.bio.symbol.AlphabetManager;
import org.biojava.bio.symbol.Symbol;
import org.biojava.bio.symbol.SymbolList;
import org.biojava.bio.symbol.Alphabet;
import org.biojava.bio.symbol.Alignment;
import org.biojava.bio.symbol.IllegalSymbolException;
import org.biojava.bio.seq.DNATools;
import java.util.*;

public class Contig extends AbstractSymbolList {

    Set<ContigTrace> _traces;
    AggregateSymbolList _composite;
    PotentialOverlaps _potentialOverlaps;

    public Contig(Trace seed) {
	_traces = new TreeSet<ContigTrace>();
	_traces.add( new ContigTrace( 1, seed));
	_composite = new AggregateSymbolList( seed.getSequence());
	_potentialOverlaps = new PotentialOverlaps();
	_potentialOverlaps.addAll( seed.getMatches() );
    }
    
    public void add(int first, Trace trace) {
	/*_composite.set( first, trace.getSequence());
	if( first < 1) {
	    for( ContigTrace ct : _traces) {
		ct.first -= (first - 1);
	    }
	    first = 1;
	}
	_traces.add( new ContigTrace( first, trace));
	_potentialOverlaps.addAll( trace.getMatches() );
	*/
    }


    public void add(Trace trace, OverlapAlignment align) {
	Alignment alignment = align.alignment;
	int existingPos, seqPos;
	Symbol gap = DNATools.getDNA().getGapSymbol();

	if( align.offset < 1) {
	    existingPos = 1;
	    seqPos = -align.offset + 2;
	} else {
	    existingPos = align.offset;
	    seqPos = 1;
	}

	for( int alignmentPos = 1; alignmentPos < alignment.length(); alignmentPos++) {
	    if( _composite.consensusSymbolAt(existingPos) != gap &&
		alignment.symbolAt("subject", alignmentPos) == gap) {
		
		insertGap( existingPos);
	    }

	    if( alignment.symbolAt("query", alignmentPos) == gap) {
		trace.insertGap( seqPos);
	    }

	    seqPos++;
	    existingPos++;
	}

	_composite.set( trace.getSequence(), align.offset);
	if( align.offset < 1) {
	    for( ContigTrace ct : _traces) {
		ct.first += -align.offset + 1;
	    }
	    _traces.add( new ContigTrace( 1, trace));
	} else {
	    _traces.add( new ContigTrace( align.offset, trace));
	}
    }

    private void insertGap(int pos) {
	int endingPos;
	int insertedGaps = 0;
	for( ContigTrace ct : _traces) {
	    if( ct.first < pos) {
		endingPos = ct.first + ct.trace.getSequence().length() - 1;
		if( endingPos >= pos) {
		    ct.trace.insertGap( pos - ct.first + 1);
		    insertedGaps++;
		}
	    } else {
		ct.first++;
	    }
	}
	_composite.insertGap(pos, insertedGaps);
    }

    public void set( Trace t, int first) {
	_traces.add( new ContigTrace( first, t));
	_composite.set( t.getSequence(), first);
    }

    public Iterator<ContigTrace> iterator() {
	return _traces.iterator();
    }

    public Trace nextPotentialOverlap() {
	return _potentialOverlaps.next();
    }

    public boolean hasNextPotentialOverlap() {
	return _potentialOverlaps.hasNext();
    }
    public String toString() {
	StringBuilder sb = new StringBuilder();
	String id;
	//         12345678901234567890
	sb.append("CONSENSUS            ");
	for( int i = 1; i <= _composite.length(); i++) {
	    try {
		sb.append( DNATools.dnaToken( _composite.consensusSymbolAt(i) ));
	    } catch( IllegalSymbolException e) {
		sb.append("?");
	    }
	}
	sb.append("\n");

	for( ContigTrace ct : _traces) {
	    id = ct.trace.getId();
	    sb.append(ct.trace.getId());
	    for( int i = id.length(); i < 20+ct.first; i++) {
		sb.append(" ");
	    }
	    
	    SymbolList seq = ct.trace.getSequence();
	    for( int i = 1; i <= seq.length(); i++) {
		try {
		    if( seq.symbolAt(i) == _composite.consensusSymbolAt(i+ct.first-1) ) {
			sb.append(".");
		    } else {
			sb.append( DNATools.dnaToken( seq.symbolAt(i)));
		    }
		} catch( IllegalSymbolException e) {
		    sb.append("?");
		}

	    }
	    //sb.append(ct.trace.getSequence().seqString());
	    sb.append("\n");
	}
	return sb.toString();
    }

    /*********************************
     * Implementation for AbstractSymbolList
     ******************************* */

    public Alphabet getAlphabet() {
	return DNATools.getDNA();
    }
    public int length() {
	return _composite.length();
    }
    public Symbol symbolAt(int index) {
	return _composite.consensusSymbolAt(index);
    }

}

class ContigTrace implements Comparable<ContigTrace> {

    public int first;
    public Trace trace;

    public ContigTrace( int first, Trace trace) {
	this.first = first;
	this.trace = trace;
    }

    public int compareTo( ContigTrace other) {
	if( other == null) {
	    return 1;
	}
	if( first == other.first && trace != null && other.trace != null) {
	    int otherLength = other.trace.getSequence().length();
	    int thisLength = trace.getSequence().length();
	    if( otherLength == thisLength) {
		return trace.getId().compareTo( other.trace.getId());
	    } else {
		return otherLength - thisLength;
	    }
	} else {
	    return first - other.first;
	}
    }
}